
// Copyright (C) 2012 Luca Piccioni
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Diagnostics;

namespace Derm.Render
{
	/// <summary>
	/// 
	/// </summary>
	[DebuggerDisplay("{mLocalModel}")]
	public class TransformState : ShaderUniformState
	{
		#region Transform State Definition

		/// <summary>
		/// This RenderObject model matrix.
		/// </summary>
		[RenderObjectUniform("ds_Model")]
		public Matrix4x4 GetModel(RenderContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");

			return (mLocalModel);
		}

		/// <summary>
		/// This RenderObject projection matrix.
		/// </summary>
		[RenderObjectUniform("ds_Projection")]
		public Matrix4x4 GetProjection(RenderContext ctx)
		{
			return (ctx.Projection);
		}

		/// <summary>
		/// This RenderObject model matrix multiplied with projection matrix.
		/// </summary>
		[RenderObjectUniform("ds_ModelProjection")]
		public Matrix4x4 GetModelProjection(RenderContext ctx)
		{
			return (ctx.Projection * GetModel(ctx));
		}

		/// <summary>
		/// The normal matrix.
		/// </summary>
		[RenderObjectUniform("ds_NormalMatrix")]
		public Matrix3x3 GetNormalMatrix(RenderContext ctx)
		{
			Matrix3x3 normalMatrix = new Matrix3x3(GetModel(ctx), 3, 3);

			return ((Matrix3x3)normalMatrix.GetInverseMatrix().Transpose());
		}

		/// <summary>
		/// The local model of this state.
		/// </summary>
		public ModelMatrix LocalModel
		{
			get { return (mLocalModel); }
		}

		/// <summary>
		/// The local model of this state.
		/// </summary>
		private ModelMatrix mLocalModel = new ModelMatrix();

		#endregion

		#region Default State

		/// <summary>
		/// The system default state for TransformState.
		/// </summary>
		public static TransformState DefaultState { get { return (new TransformState()); } }

		#endregion

		#region ShaderUniformState Overrides

		/// <summary>
		/// The identifier for the blend state.
		/// </summary>
		public static string StateId = "Derm.Shaders.TransformState";

		/// <summary>
		/// The identifier of this RenderState.
		/// </summary>
		public override string Id { get { return (StateId); } }

		/// <summary>
		/// Flag indicating whether the state is context-bound.
		/// </summary>
		/// <remarks>
		/// It returns always true.
		/// </remarks>
		public override bool IsContextBound { get { return (false); } }

		/// <summary>
		/// Merge this state with another one.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for merging. It declares the renderer capabilities.
		/// </param>
		/// <param name="state">
		/// A <see cref="RenderState"/> having the same <see cref="RenderState.Id"/> of this state. If this state depends
		/// on the information of <paramref name="state"/>, this is the entry point for modify this state.
		/// </param>
		public override void Merge(RenderContext ctx, RenderState state)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (state == null)
				throw new ArgumentNullException("state");
			if (state.Id != Id)
				throw new ArgumentException("state id mismatch", "state");

			TransformState previousState = (TransformState) state;

			mLocalModel.Set(previousState.LocalModel * mLocalModel);
		}

		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <param name="other">
		/// A <see cref="RenderState"/> to compare to this RenderState.
		/// </param>
		/// <returns>
		/// It returns true if the current object is equal to <paramref name="other"/>.
		/// </returns>
		/// <remarks>
		/// <para>
		/// This method test only whether <paramref name="other"/> type equals to this type.
		/// </para>
		/// </remarks>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="other"/> is null.
		/// </exception>
		public override bool Equals(IRenderState other)
		{
			if (base.Equals(other) == false)
				return (false);
			Debug.Assert(other is TransformState);

			TransformState otherState = (TransformState) other;

			return (false);
		}

		/// <summary>
		/// Performs a deep copy of this <see cref="IRenderState"/>.
		/// </summary>
		/// <returns>
		/// It returns the equivalent of this <see cref="IRenderState"/>, but all objects referenced
		/// are not referred by both instances.
		/// </returns>
		public override IRenderState Copy()
		{
			TransformState copiedState = (TransformState) base.Copy();

			copiedState.mLocalModel = new ModelMatrix(mLocalModel);

			return (copiedState);
		}
		
		#endregion
	}
}
